# GOB.PY - Goal oriented behaviour code
# HIT3046 AI for GAMES
# ------- Assignment 2
# Authors: Mike Blackney, 0906123
#          Alex Bruce, 5409438

import math # Used for pythagorean calcs
import io
import pos

from defaults import *

class ACTIONAGENT (object):
    "All GOB agents and actions derive from this abstract class."

    def __init__( self, *args ):
        self.actionList = list( )
        self.currAction = None
        
    def PushAction( self, action ):
        # Push a new action to the top of the stack:
        if len(self.actionList) > 0:
            self.actionList[len(self.actionList)-1].Leave( )
            
        self.actionList.append( action )
        action.parent = self
        #action.Enter( )
        
    def PopAction( self ):
        # Take the current action off the stack:
        if len( self.actionList ) > 0:
            #self.actionList[len(self.actionList)-1].Leave( )
            self.actionList.pop( )
        #if len( self.actionList ) > 0:
            #self.actionList[len(self.actionList)-1].Enter( )

    def ClearActions( self ):
        # Empty the action list
        while len(self.actionList) > 0:
            self.actionList.pop( )

    def Location( self ):
        print "virtual ACTIONAGENT.Location called!"
        raise SystemExit
    def Owner( self ):
        print "virtual ACTIONAGENT.Owner called!"
        raise SystemExit
    def Tick( self, currTime, deltaTime ):
        # First we reverse our actions prior to sort.  We do this because
        # if all actions have the same priority, we don't want them to change
        # every tick.
        self.actionList.reverse( )
        # Sort our action priority queue:
        self.actionList.sort( )
        # And reverse it because low priority value means most important:
        self.actionList.reverse( )
        # If the priority action has changed,
        if len( self.actionList ) > 0 \
           and self.actionList[len(self.actionList)-1] is not self.currAction:
            # leave current action:
            if self.currAction is not None:
                self.currAction.Leave( )
            # set current:
            self.currAction = self.actionList[len(self.actionList)-1]
            # enter new action:
            self.currAction.Enter( )
        # Update action:
        if self.currAction is not None:
            self.currAction.Tick( currTime, deltaTime )

class ACTION (ACTIONAGENT):
    "An abstract action that an Agent can be in.  Can have sub-actions."

    def Enter( self ):
        print "virtual ACTION.Enter called!"
        raise SystemExit
    def Leave( self ):
        print "virtual ACTION.Leave called!"
        raise SystemExit
    def Priority( self ):
        return 1000
    def __cmp__( self, other ):
        return cmp( self.Priority( ), other.Priority( ) )
    def Tick( self, currTime, deltaTime ):
        ACTIONAGENT.Tick( self, currTime, deltaTime )
        #if len(self.actionList) > 0:
        #    self.actionList[len(self.actionList)-1].Tick( currTime, deltaTime );
    def Location( self ):
        return self.parent.Location( )
    def Owner( self ):
        return self.parent.Owner( )
    def MoveTo( self, target, deltaTime, flee=False ):
        self.parent.MoveTo( target, deltaTime, flee )

class GAMEAGENT (ACTIONAGENT):
    "All classes that communicate/interact derive from this abstract class."

    # Most AGENTs need sprites:
    spr_sprite = None
    spr_action = None

    # This static list lists all the gameagents in the game:
    agent_list = list( )
    
    ClassType = "ABSTRACT" # Leaf subclasses should redefine this

    def __init__( self, *args ): # Initialiser
        # Add this object to the agent list:
        self.agent_list.append( self )
        # Chain up:
        ACTIONAGENT.__init__( self )
        # All AGENTs have position coords:
        self.location = pos.Pos(0,0)
        # Not deleted, duh:
        self.deleted = False

    def Location( self ):
        return self.location
    
    def Owner( self ):
        return self

    def Destroy( self ): # When called, removes from the simulation
        # If we've been deleted, we're cool:
        if self.deleted: return
        # remove from the agent list:
        self.agent_list.remove( self )
        # Clear the sprites:
        self.spr_sprite = self.ClearSprite( self.spr_sprite )
        self.spr_action = self.ClearSprite( self.spr_action )
        # Mark ourselves as deleted (in case we're still referenced):
        self.deleted = True
    
    def PositionSprites( self ):
        # Move the sprites into the correct positions:
        if self.spr_sprite is not None:
            self.spr_sprite.rect.center = self.location.to_list( )
        if self.spr_action is not None:
            self.spr_action.rect.center = ( self.location.x + ACTION_XOFF, \
                                           self.location.y + ACTION_YOFF )

    def isPenetrating( self, agent ):
        # Returns true if we're colliding with another agent.
        if self.spr_sprite == None or agent.spr_sprite == None:
            return False
        return self.spr_sprite.rect.colliderect( agent.spr_sprite.rect )

    def Tick( self, currTime, deltaTime ):
        # Update action:
        ACTIONAGENT.Tick( self, currTime, deltaTime )
        # Update sprites:
        self.PositionSprites( )

    def MoveTo( self, target, deltaTime, flee=False ):
        # Move 'speed' units towards target location
        delta = target - self.location
        delta = delta.normalise( ) * deltaTime * self.GetSpeed( )
        if not flee:
            self.location += delta
        else:
            self.location -= delta

    def GetSpeed( self ):
        return DEFAULT_SPEED

    def SetSprite( self, sprite ):
        io.FEEDBACK( ).spriteList.add( sprite )
        return sprite

    def ClearSprite( self, sprite ):
        io.FEEDBACK( ).spriteList.remove( sprite )
        return None

    def PrintText( self, font, background ):
        pass
